//--------------------------------------------------------------------------------------
// Constant Buffers
//--------------------------------------------------------------------------------------

cbuffer cbPerFrame : register( b0 )
{
	matrix g_mWorld;                   // World matrix for object
	matrix g_mView;					 // View matrix for object
	matrix g_mProj;					 // View matrix for object
	
	float4 g_vEyePos;
	
	float4 g_vLightColor[3];
	float4 g_vLightPos[3];

	float4 g_Ambient;
	float4 g_Diffuse;
	float4 g_Specular;
	float4 g_vars; //g_vars[3] == PI
	//float g_kDiffuse;
	//float g_PhongExp;
	//int   g_NumLights;
	//int   dummy;
};

Texture2D txDiffuse : register( t0 );
SamplerState samLinear : register( s0 );

//------------------------------------------------------------
struct VS_INPUT2
{
	float3 vPosition	:POSITION;
	float2 vTex			:TEXCOORD0;
};

struct VS_OUTPUT
{
	float3 vPosition	:WORLDPOS;
	float2 vTex			:TEXCOORD0;
};
struct HS_OUTPUT
{
    float3 vPosition	: BEZIERPOS;
	float2 vTex			:TEXCOORD0;
};
struct DS_OUTPUT
{
	float4 vPosition	: POSITION;
	float3 v3DPos		: WORLDPOS;
	float2 vTex			:TEXCOORD0;
	float3 vNormal		:NORMAL;
	float4 vColor		:COLOR0;
};
struct GS_OUTPUT
{
	float4 vPosition	: SV_POSITION;
	float4 vColor		:COLOR0;
	float3 v3DPos		:WORLDPOS;
	float3 vNormal		:NORMAL;
	float2 vTex			:TEXCOORD0;
};
struct PS_INPUT
{
	float4 vPosition	: SV_POSITION;
	float4 vColor		:COLOR0;
	float2 vTex			:TEXCOORD0;
};

float4 PhongModel(float4 Pos, float3 Normal)
{
	//float3 g_vEyePos = float3(3.0,3.0,3.5);
	int g_NumLights = 3;
	//float3 g_vLightPos[3] = {float3(0, 6, 0),float3(10, 0, 0),float3(-10, -10, 0)};
	//float4 g_vLightColor[3] = {float4(0.6, 0.6, 0.6, 1),float4(0, 0.2, 0.7, 1),float4(1, 0.3, 0, 1)};
	
	//float4 g_Ambient  = float4(0.3, 0.3, 0.3, 1);
	//float4 g_Diffuse  = float4(0.7, 0.7, 0.7, 1);
	//float4 g_Specular = float4(0.65, 0.65, 0.25, 1);
	float g_kDiffuse = 1.0f;
	float g_PhongExp = 128.0f;
	//float g_kDiffuse = g_vars[0];

	float4 Color = g_Ambient;
	float3 EyeToPos = normalize(Pos - g_vEyePos.xyz);
	float3 LightToPos = float3(0,0,0), Reflected = float3(0,0,0);
	float4 LightPos = float4(0.0,0.0,0.0,1.0);
	float DiffuseIntensity = 0, Spec = 0;
	
	for (int i = 0; i < g_NumLights; i++)
	{
		//LightPos = float4(g_vLightPos[i], 1);
		LightPos = g_vLightPos[i];
		LightToPos = normalize(Pos - LightPos);
		// Compute the diffuse component
		DiffuseIntensity = saturate(dot(-LightToPos, Normal));
		// Compute the specular component
		Reflected = normalize(LightToPos - 2*dot(Normal, LightToPos) * Normal);
		Spec = saturate(dot(-Reflected, EyeToPos));
		Spec = pow(max(Spec, 0), g_PhongExp);
		Color = Color + g_vLightColor[i] * ((DiffuseIntensity * g_Diffuse) + (Spec * g_Specular));
	}

	return Color;
}

//--------------------------------------------------------------------------
//Vertex shader
//--------------------------------------------------------------------------
VS_OUTPUT SmoothVS(VS_INPUT2 In)
{	
	VS_OUTPUT result;
	result.vPosition = In.vPosition;
	result.vTex = In.vTex;
    return result;  
}


//----------------------------------------------------------------------------------
//	Basic Hull Shader
//
//----------------------------------------------------------------------------------

struct HS_CONSTANT_DATA_OUTPUT
{
    float Edges[4]			: SV_TessFactor;
    float Inside[2]			: SV_InsideTessFactor;
};

HS_CONSTANT_DATA_OUTPUT MyConstantsHS32( InputPatch<VS_OUTPUT, 4> ip,
                                                 uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;
    
    float TessAmount = 16.0f;
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
   
    return Output;
}


[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("MyConstantsHS32")]
HS_OUTPUT SmoothHS32( InputPatch<VS_OUTPUT, 4> p, 
                                     uint i : SV_OutputControlPointID,
                                     uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vPosition=p[i].vPosition;
	Output.vTex=p[i].vTex;
    return Output;
}

HS_CONSTANT_DATA_OUTPUT MyConstantsHS10( InputPatch<VS_OUTPUT, 4> ip,
                                                 uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;
    
    float TessAmount = 10.0f;
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
   
    return Output;
}


[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("MyConstantsHS10")]
HS_OUTPUT SmoothHS10( InputPatch<VS_OUTPUT, 4> p, 
                                     uint i : SV_OutputControlPointID,
                                     uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vPosition=p[i].vPosition;
	Output.vTex=p[i].vTex;
    return Output;
}

//----------------------------------------------------------------------------------


HS_CONSTANT_DATA_OUTPUT MyConstantsHS64( InputPatch<VS_OUTPUT, 4> ip,
                                                 uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;
    
    float TessAmount = 64.0f;
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
   
    return Output;
}


//----------------------------------------------------------------------------------
[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("MyConstantsHS64")]
HS_OUTPUT SmoothHS64( InputPatch<VS_OUTPUT, 4> p, 
                                     uint i : SV_OutputControlPointID,
                                     uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vPosition=p[i].vPosition;
	Output.vTex=p[i].vTex;
    return Output;
}

//----------------------------------------------------------------------------------

HS_CONSTANT_DATA_OUTPUT MyConstantsHS2( InputPatch<VS_OUTPUT, 4> ip,
                                                 uint PatchID : SV_PrimitiveID )
{	
    HS_CONSTANT_DATA_OUTPUT Output;
    
    float TessAmount = 2.0f;
    Output.Edges[0] = Output.Edges[1] = Output.Edges[2] = Output.Edges[3] = TessAmount;
    Output.Inside[0] = Output.Inside[1] = TessAmount;
   
    return Output;
}


[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(4)]
[patchconstantfunc("MyConstantsHS2")]
HS_OUTPUT SmoothHS2( InputPatch<VS_OUTPUT, 4> p, 
                                     uint i : SV_OutputControlPointID,
                                     uint PatchID : SV_PrimitiveID )
{
    HS_OUTPUT Output;
    Output.vPosition=p[i].vPosition;
	Output.vTex=p[i].vTex;
    return Output;
}


//--------------------------------------------------------------------
//Domain shader
//--------------------------------------------------------------------

[domain("quad")]
DS_OUTPUT SmoothDS( HS_CONSTANT_DATA_OUTPUT input, 
                        float2 UV : SV_DomainLocation,
                        const OutputPatch<HS_OUTPUT, 4> bezpatch )
{

    DS_OUTPUT Output;
		
	float p = (bezpatch[3].vPosition.x-bezpatch[0].vPosition.x)*UV.x;
	float t = (bezpatch[3].vPosition.y-bezpatch[0].vPosition.y)*UV.y;
	
	float r;
	
	if ( p == 0.0 )
		p = 0.001;
	if ( p == g_vars[3] )
		p=g_vars[3]-0.001;
		
		
## EQ ##
		
	float3 WorldPos=float3(0.0,0.0,0.0);
	
	WorldPos.x = r*cos(t)*sin(p);
	WorldPos.y = r*sin(t)*sin(p);
	WorldPos.z = r*cos(p);
	
	Output.v3DPos = mul(float4(WorldPos,1.0f), g_mWorld);
	Output.vPosition = mul(float4(Output.v3DPos,1.0f), g_mView);
	Output.vTex.x = UV.x;
	Output.vTex.y = UV.y;
	
	float h = 0.0001;
		
	float dr,dt,dp;
	float f11,f12,f21,f22;
	
## DERIVATIVES ##
	
	dt = (f11-f12)/(2*h);
	dp = (f21-f22)/(2*h);
	
	if ( r == 0.0 )
		r=h;
		
	float cost = cos(t);
	float sint = sin(t);
	float cosp = cos(p);
	float sinp = sin(p);
	
	/*if ( p == 0.0 || p == g_vars[3] )
	{
		sinp = 0.001;
		//float3 tu = float3( -sin(t), cos(t), 0 )*dt;
		//float3 pu = float3( cos(t)*cos(p), sin(t)*cos(p), -sin(p) )*dp;
	
		//Output.vNormal = normalize( cross( pu, tu ) );
	}
	//else*/
		Output.vNormal = -normalize(	float3(	cost*sinp*dr - (sint/(r*sinp))*dt + ((cost*cosp)/r)*dp,
										sint*sinp*dr + (cost/(r*sinp))*dt + ((sint*cosp)/r)*dp,
										cosp*dr - (sinp/r)*dp
									  ));
	
	Output.vColor = float4(0.9,0.9,0.9,1.0);
	
	Output.vNormal = mul(float4(Output.vNormal,1.0f), g_mWorld);
	
	return Output;    
}


[domain("quad")]
DS_OUTPUT SmoothDS2( HS_CONSTANT_DATA_OUTPUT input, 
                        float2 UV : SV_DomainLocation,
                        const OutputPatch<HS_OUTPUT, 4> bezpatch )
{

    DS_OUTPUT Output;
	
	float3 p1 = (bezpatch[1].vPosition-bezpatch[0].vPosition)*UV.x+bezpatch[0].vPosition;
	float3 p2 = (bezpatch[3].vPosition-bezpatch[2].vPosition)*UV.x+bezpatch[2].vPosition;
	
	float3 WorldPos = (p2-p1)*UV.y+p1;
	
	Output.v3DPos = mul(float4(WorldPos,1.0f), g_mWorld);
	Output.vPosition = mul(float4(Output.v3DPos,1.0f), g_mView);
	Output.vTex.x = UV.x;
	Output.vTex.y = UV.y;
	
	Output.vColor = float4(0.9,0.9,0.9,1.0);
	
	Output.vNormal = float4(0.0,0.0,0.0,0.0);
	
	return Output;    
}


//--------------------------------------------------------------------
//Geometry shader
//--------------------------------------------------------------------


//---------------------------------------------------------------------------

[maxvertexcount(3)]
void SmoothGS( triangle DS_OUTPUT input[3], inout TriangleStream<GS_OUTPUT> TriStream )
{	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vPosition = input[0].vPosition;
	vP0.v3DPos =input[0].v3DPos;
	vP0.vNormal = input[0].vNormal;
	vP0.vColor = input[0].vColor;
	vP0.vTex = input[0].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[1].vPosition;
	vP0.v3DPos =input[1].v3DPos;
	vP0.vNormal = input[1].vNormal;
	vP0.vColor = input[1].vColor;
	vP0.vTex = input[1].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[2].vPosition;
    vP0.v3DPos =input[2].v3DPos;
	vP0.vNormal = input[2].vNormal;
	vP0.vColor = input[2].vColor;
	vP0.vTex = input[2].vTex;
	TriStream.Append( vP0 );
	
	TriStream.RestartStrip();
}


[maxvertexcount(3)]
void SmoothGS2( triangle DS_OUTPUT input[3], inout TriangleStream<GS_OUTPUT> TriStream )
{	
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( faceNormal );
    float4 color = float4(0.9,0.9,0.9,1.0);
	
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vPosition = input[0].vPosition;
	vP0.v3DPos    = input[0].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[0].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[1].vPosition;
	vP0.v3DPos    = input[1].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[1].vTex;
	TriStream.Append( vP0 );
    
    vP0.vPosition = input[2].vPosition;
    vP0.v3DPos    = input[2].v3DPos;
    vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[2].vTex;
	TriStream.Append( vP0 );
	
	TriStream.RestartStrip();
}


[maxvertexcount(6)]
void SmoothGSGrass( triangle DS_OUTPUT input[3], inout LineStream<GS_OUTPUT> LnStream )
{	
    
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( faceNormal );
    float grassH = 0.3;
	float4 color = float4(0.2,0.8,0.6,1.0);
	float4 tmp;
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vColor	  = color;
    vP0.vPosition = input[0].vPosition;
	vP0.vColor    = color;
	vP0.v3DPos 	  = input[0].v3DPos;
	vP0.vNormal	  = input[0].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos    = input[0].v3DPos+input[0].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
    
    vP0.vPosition = input[1].vPosition;
	vP0.vColor    = color;
	vP0.v3DPos    = input[1].v3DPos;
	vP0.vNormal   = input[1].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[1].v3DPos+input[1].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
	
    LnStream.RestartStrip();
    
    vP0.vPosition = input[2].vPosition;
    vP0.vColor    = color;
    vP0.v3DPos    = input[2].v3DPos;
	vP0.vNormal   = input[2].vNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos    = input[2].v3DPos+input[2].vNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
}

[maxvertexcount(6)]
void SmoothGSGrass2( triangle DS_OUTPUT input[3], inout LineStream<GS_OUTPUT> LnStream )
{	
    
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( faceNormal );
    float grassH = 0.3;
	float4 color = float4(0.2,0.8,0.6,1.0);
	float4 tmp;
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
        
    vP0.vColor    = color;
    vP0.vPosition = input[0].vPosition;
	vP0.vColor    = color;
	vP0.v3DPos    = input[0].v3DPos;
	vP0.vNormal   = faceNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[0].v3DPos+faceNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
    
    vP0.vPosition = input[1].vPosition;
	vP0.vColor    = color;
	vP0.v3DPos    = input[1].v3DPos;
	vP0.vNormal   = faceNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos = input[1].v3DPos+faceNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
	
    LnStream.RestartStrip();
    
    vP0.vPosition = input[2].vPosition;
    vP0.vColor    = color;
    vP0.v3DPos    = input[2].v3DPos;
	vP0.vNormal   = faceNormal;
    LnStream.Append( vP0 );
    
    vP0.v3DPos    = input[2].v3DPos+faceNormal*grassH;
	vP0.vPosition = mul(float4(vP0.v3DPos,1.0f), g_mView);
	LnStream.Append( vP0 );
    
	LnStream.RestartStrip();
}

[maxvertexcount(4)]
void SmoothGS3( triangle DS_OUTPUT input[3], inout LineStream<GS_OUTPUT> LnStream )
{	
    float3 faceEdgeA = input[1].v3DPos - input[0].v3DPos;
    float3 faceEdgeB = input[2].v3DPos - input[0].v3DPos;
    float3 faceNormal =cross(faceEdgeA, faceEdgeB);
    faceNormal = normalize( faceNormal );
    float4 color = float4(0.9,0.9,0.9,1.0);
	
	
	GS_OUTPUT vP0 = (GS_OUTPUT)0;
    
	vP0.vPosition = input[0].vPosition;
	vP0.v3DPos    = input[0].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[0].vTex;
	LnStream.Append( vP0 );
	
	float3 v1=input[0].v3DPos-input[1].v3DPos;
	float3 v2=input[0].v3DPos-input[2].v3DPos;
	float3 v3=input[1].v3DPos-input[2].v3DPos;
	float d1 = sqrt( v1.x*v1.x + v1.y*v1.y + v1.z*v1.z);
	float d2 = sqrt( v2.x*v2.x + v2.y*v2.y + v2.z*v2.z);
	float d3 = sqrt( v3.x*v3.x + v3.y*v3.y + v3.z*v3.z);
	
	int i,j,k;
	if ( d1 > d3 && d1 > d2 )
	{
		i=2;j=1,k=2;
	}
	else if ( d2 > d3 && d2 > d1 )
	{
		i=1;j=1,k=2;
	}
	else if ( d3 > d1 && d3 > d2 )
	{
		i=1;j=0,k=2;
	}
	vP0.vPosition = input[i].vPosition;
	vP0.v3DPos    = input[i].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[i].vTex;
	LnStream.Append( vP0 );
	
	LnStream.RestartStrip();
	
	vP0.vPosition = input[j].vPosition;
	vP0.v3DPos    = input[j].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[j].vTex;
	LnStream.Append( vP0 );
	
	vP0.vPosition = input[k].vPosition;
	vP0.v3DPos    = input[k].v3DPos;
	vP0.vNormal   = faceNormal;
	vP0.vColor    = color;
	vP0.vTex      = input[k].vTex;
	LnStream.Append( vP0 );
		
	LnStream.RestartStrip();
}

//--------------------------------------------------------------------
//Geometry shader
//--------------------------------------------------------------------

//---------------------------------------------

float4 SmoothPS(GS_OUTPUT In) : SV_TARGET
{
	return In.vColor*PhongModel(float4(In.v3DPos, 1.0),In.vNormal);
}

float4 SmoothPS2(GS_OUTPUT In) : SV_TARGET
{
	return In.vColor;
}

float4 SmoothPS3(GS_OUTPUT In) : SV_TARGET
{
	return float4(0.0,0.0,0.0,1.0);
	//return txDiffuse.Sample( samLinear, In.vTex );
}

